/*------------------------------------------------------------------------------
$module:      CharArray
$package:     CseApi
$purpose:     TCharArray class definition
$author:      WAYNE WORK STDIO
$CreateAt:    08/07/04
$LastModify:
$license:     Mozilla Public License Version 1.1
------------------------------------------------------------------------------*/

#include "CseApi_.h"

#define CSE_FORMAT_BUFF            4096
#define STR_MARSH_LOOP_COUNT       8     // max get 8*4 byte to caculate hash

#ifdef _UNICODE
  #define STRTOD(x,ptr)            wcstod(x,ptr)
#else
  #define STRTOD(x,ptr)            strtod(x,ptr)
#endif

STATIC TInt32 MaskList[33] = { 0x0000,0x0001,0x0003,0x0007,0x000F,0x001F,0x003F,0x007F,
                               0x00FF,0x01FF,0x03FF,0x07FF,0x0FFF,0x1FFF,0x3FFF,0x7FFF,
0x0000FFFF,0x0001FFFF,0x0003FFFF,0x0007FFFF,0x000FFFFF,0x001FFFFF,0x003FFFFF,0x007FFFFF,
0x00FFFFFF,0x01FFFFFF,0x03FFFFFF,0x07FFFFFF,0x0FFFFFFF,0x1FFFFFFF,0x3FFFFFFF,0x7FFFFFFF,
0xFFFFFFFF };

TInt32 MAKE_FLAG(TInt32 iFlag, TInt32 i, int iOffset, int n)
{
    ASSERT_TRUE(iOffset >= 0 && iOffset <= 31);
    ASSERT_TRUE(n >= 1 && (iOffset + n) <= 32);

    TInt32 iMask = MaskList[n] << iOffset;
    return (iFlag & (~iMask)) | ((i << iOffset) & iMask);
}

TInt32 READ_FLAG(TInt32 iFlag, int iOffset, int n)
{
    ASSERT_TRUE(iOffset >= 0 && iOffset <= 31);
    ASSERT_TRUE(n >= 1 && (iOffset + n) <= 32);
    return ((TUint32)iFlag >> iOffset) & MaskList[n];
}

#ifdef _UNICODE

TCharArray Cse_StrFromAnsi(const char *sour, int n)
{
    if (sour == NULL) n = 0;
    if (n == 0) return CseEmptyStr;

    int iLen = n;
    if (n < 0)  // get length of ansi string
    {
        iLen = mbstowcs(NULL,sour,0);
        if (iLen == -1)
            throw LOG_CSE_ERR(CSE_VALUE_ERR,_T("maps multibyte-character string to wide-character string error"));
    }
    TCharArray ret(_T('\0'),iLen);

    // translate ANSI to Unicode
    mbstowcs(ret.addr(),sour,iLen);

    if (n < 0)
    {   // terminated by '\0'
        iLen = ret.len();
        if (iLen > 0 && ret[-1] == _T('\0'))
            ret.truncate(iLen-1);
    }
	return TCharArray(CUT_FROM,ret);
}

TCharArray Cse_StrFromUnicode(const TWchar *sour, int n)
{
    return TCharArray(sour,n);
}

#else

TCharArray Cse_StrFromAnsi(const char *sour, int n)
{
    return TCharArray(sour,n);
}

TCharArray Cse_StrFromUnicode(const TWchar *sour, int n)
{
    if (sour == NULL) n = 0;
    if (n == 0) return CseEmptyStr;

    int iLen = n;
    if (n < 0)  // get length of ansi string
    {
        iLen = wcstombs(NULL,sour,0);
        if (iLen == -1)
        {
            TCharArray sErr = TCharArray::format(_T("maps wide-character string to multibyte-character string error"));
            throw LOG_CSE_ERR(CSE_VALUE_ERR,sErr);
        }
    }
    TCharArray ret(_T('\0'),iLen);

    // translate Unicode to ANSI
    wcstombs(ret.addr(),sour,iLen);

    if (n < 0)
    {   // terminated by '\0'
        iLen = ret.len();
        if (iLen > 0 && ret[-1] == _T('\0'))
            ret.truncate(iLen-1);
    }
	return TCharArray(CUT_FROM,ret);
}

#endif

extern TXchar *__EmptyString;
extern const TCharArray& CseEmptyStr;

extern TCharArray *Cse_LastErrName;
extern TCharArray *Cse_LastErrDesc;

#ifndef CLOSE_REF_CHECK
  extern TInt32 gTotalCharArrayCount;
#endif

extern TCriticalSection *CseCharBuffLock;

class TCharBuffLocker
{
public:
    TCharBuffLocker(void)  { CseCharBuffLock->enter(); }
    ~TCharBuffLocker(void) { CseCharBuffLock->leave(); }
};

void LogErrNameAndDesc(const TCharArray& name, const TCharArray& desc)
{
    TCharBuffLocker lock;
    *Cse_LastErrName = name;
    *Cse_LastErrDesc = desc;
}

STATIC int StrNCompare(const TXchar* s1, int iLen1, const TXchar* s2, int iLen2)
{
    if (iLen1 == iLen2)
    {
        if (iLen1 == 0 || s1 == s2) return 0;
        else return memcmp(s1,s2,iLen1 * sizeof(TXchar));
    }
    else
    {
        int i;
        if (iLen1 < iLen2)
        {
            if (iLen1 == 0) return -1;

            i = memcmp(s1,s2,iLen1 * sizeof(TXchar));
            if (i == 0) return -1;
            else return i;
        }
        else
        {
            if (iLen2 == 0) return 1;

            i = memcmp(s1,s2,iLen2 * sizeof(TXchar));
            if (i == 0) return 1;
            else return i;
        }
    }
}

STATIC TXchar *NewCseCharBlock(TXchar ch, int n)
{
    if (n <= 0)
        return __EmptyString;
    else
    {
        int iLen = sizeof(TUint32[2]) + (n + 1) * sizeof(TXchar);
        TUint32 *pLen = (TUint32 *) new char[iLen];
        pLen[0] = n;
        pLen[1] = n;
        
        TXchar *ret = (TXchar *) &pLen[2];
        TXchar *pCh = ret;
        
        for (int i = 0; i < n; i++,pCh++) *pCh = ch;
        *pCh = _T('\0');
        
        return ret;
    }
}

STATIC TXchar *NewCseCharBlock(const TXchar *pSour, int iLen=-1, int iExt=0)
{
    ASSERT_TRUE(iExt >= 0);
    if (iLen == -1)
    {
        if (NULL == pSour)
            iLen = 0;
        else iLen = STRLEN(pSour);
    }
    else if (iLen < 0)
        throw LOG_CSE_ERR(CSE_VALUE_ERR,_T("invalid TCharArray address"));
    
    int iNewLen = iLen + iExt;

    if (iNewLen == 0 || (pSour == NULL && iLen > 0))
        return __EmptyString;
    else
    {

        int iNew = sizeof(TUint32[2]) + (iNewLen + 1) * sizeof(TXchar);
        TUint32 *pLen = (TUint32 *) new char[iNew];
#ifdef _DEBUG
        if (pLen == NULL)
            PRINTF(_T("no memory or stack overflow\n"));
#endif
        pLen[0] = iNewLen;
        pLen[1] = iLen;
        TXchar *ret = (TXchar *) &pLen[2];

        // copy string context
        TXchar *pCh = ret;
        for (int i = 0; i < iLen; i++,pCh++,pSour++) *pCh = *pSour;
        *pCh = _T('\0');
        
        return ret;
    }
}

void TryFreeCharBlock(TXchar *pCh)
{
    if (pCh != __EmptyString)
    {
        char *SourAddr = (char *)pCh - sizeof(TUint32[2]);
        delete []SourAddr;
    }
}

TCharArray::TCharArray()
{
    m_data = __EmptyString;
#ifndef CLOSE_REF_CHECK
    gTotalCharArrayCount++;
#endif
}

TCharArray::TCharArray(int MagicNum, TCharArray& str)
{   // MagicNum = 20051206 means cut from
    CHECK_MSG(MagicNum == 20051206,_T("unauthorized call"));

    m_data = str.transfer(__EmptyString);
#ifndef CLOSE_REF_CHECK
    gTotalCharArrayCount++;
#endif
}

TCharArray::TCharArray(const TCharArray& s)
{
    TCharBuffLocker lock;
    m_data = NewCseCharBlock(s.m_data,s.len());
#ifndef CLOSE_REF_CHECK
    gTotalCharArrayCount++;
#endif
}

TCharArray::TCharArray(TXchar ch, int count)
{
    m_data = NewCseCharBlock(ch,count);
#ifndef CLOSE_REF_CHECK
    gTotalCharArrayCount++;
#endif
}

TCharArray::TCharArray(const TXchar* pSour, int iLen, int iExt)
{
    if (pSour == NULL && iExt == 0)
        m_data = __EmptyString;
    else
    {
        TCharBuffLocker lock;
        m_data = NewCseCharBlock(pSour,iLen,iExt);
    }
#ifndef CLOSE_REF_CHECK
    gTotalCharArrayCount++;
#endif
}

TCharArray::~TCharArray()
{
    TryFreeCharBlock(m_data);
#ifndef CLOSE_REF_CHECK
    gTotalCharArrayCount--;
#endif
}

TCharArray& TCharArray::joinEx(const TXchar* pSour, int iSourLen)
{
    if (iSourLen <= 0) return *this;  // no need join

    TUint32 *pLen = (TUint32 *) ((char *)m_data - sizeof(TUint32[2]));
    int iTargLen = pLen[1];
    int iTotal   = iTargLen + iSourLen;

    if ((int)pLen[0] >= iTotal)
    {   // have enough space
        TXchar *pTarg = m_data + iTargLen;
        for (int i = 0; i < iSourLen; i++,pTarg++,pSour++)
            *pTarg = *pSour;
        *pTarg = _T('\0');

        pLen[1] = iTotal;
    }
    else
    {   // add more space to hurry up continuous string adding
        int iPad = (iTotal + 1) / 2;

        // create enough space and join
        TCharArray ret(m_data,iTargLen,iSourLen + iPad);
        ret.join(pSour,iSourLen);

        // exchange the result and self
        ret.m_data = transfer(ret.m_data);
    }
    return *this;
}

TCharArray& TCharArray::join(const TCharArray& str, int index)
{
    TCharBuffLocker lock;

    int iSourLen = str.len();
    if (index < 0)
    {
        index = iSourLen + index;
        if (index < 0) index = 0;
    }

    return joinEx(str.m_data+index,iSourLen-index);
}

TCharArray& TCharArray::join(const TXchar* pSour, int iSourLen)
{
    TCharBuffLocker lock;
    int iSourLen_ = (iSourLen < 0)? ((pSour == NULL)? 0:STRLEN(pSour)): iSourLen;
    return joinEx(pSour,iSourLen_);
}

TCharArray& TCharArray::operator=(const TCharArray& str)
{
    TCharBuffLocker lock;
    
    int iSourLen = str.len();
    TUint32 *pLen = (TUint32 *) ((char *)m_data - sizeof(TUint32[2]));
    
    if ((int)pLen[0] >= iSourLen)
    {   // has enough space
        TXchar *pTarg = m_data;
        TXchar *pSour = str.m_data;
        for (int i = 0; i < iSourLen; i++,pTarg++,pSour++)
            *pTarg = *pSour;
        *pTarg = _T('\0');
        
        pLen[1] = iSourLen;
    }
    else
    {
        TryFreeCharBlock(m_data);
        m_data = NewCseCharBlock(str.m_data,iSourLen);
    }
    return *this;
}

TCharArray& TCharArray::operator=(const TXchar* pSour)
{
    TCharBuffLocker lock;

    int iSourLen = (pSour == NULL)? 0:STRLEN(pSour);
    TUint32 *pLen = (TUint32 *) ((char *)m_data - sizeof(TUint32[2]));

    if ((int)pLen[0] >= iSourLen)
    {   // has enough space
        TXchar *pTarg = m_data;
        for (int i = 0; i < iSourLen; i++,pTarg++,pSour++)
            *pTarg = *pSour;
        *pTarg = _T('\0');

        pLen[1] = iSourLen;
    }
    else
    {
        TryFreeCharBlock(m_data);
        m_data = NewCseCharBlock(pSour,iSourLen);
    }
    return *this;
}

TCharArray& TCharArray::operator=(TXchar ch)
{
    TCharBuffLocker lock;

    TUint32 *pLen = (TUint32 *) ((char *)m_data - sizeof(TUint32[2]));
    if (pLen[0] >= 1)
    {
        m_data[0] = ch;
        m_data[1] = _T('\0');
        pLen[1] = 1;
    }
    else
    {
        TryFreeCharBlock(m_data);
        m_data = NewCseCharBlock(ch,1);
    }
    return *this;
}

void TCharArray::operator+=(const TCharArray& str)
{
    TCharBuffLocker lock;
    joinEx(str.m_data,str.len());
}

void TCharArray::operator+=(const TXchar* pSour)
{
    join(pSour);
}

void TCharArray::operator+=(TXchar ch)
{
    join(&ch,1);
}

TCharArray TCharArray::multi(int n) const
{
    int iSourLen = len();
    int iTargLen = n * iSourLen;
    if (iTargLen <= 0) return CseEmptyStr;

    TCharArray ret;
    ret.m_data = NewCseCharBlock(_T('\0'),iTargLen);
    *(TUint32 *)((char *)ret.m_data - sizeof(TUint32)) = 0;

    TCharBuffLocker lock;
    for (int i = 0; i < n; i++)
        ret.joinEx(m_data,iSourLen);
    return TCharArray(CUT_FROM,ret);
}

TXchar& TCharArray::sub(int index) const
{
    TCharBuffLocker lock;

    int iLen = len();
    if (index < 0) index = iLen + index;

    CHECK_MSG(index >= 0 && index <= iLen,_T("access TCharArray out of range"));
    return m_data[index];
}

TXchar& TCharArray::operator[](int index) const
{
    return sub(index);
}

TCharArray::operator TXchar*() const
{
    return m_data;
}

TXchar *TCharArray::addr() const
{
    return m_data;
}

int TCharArray::len() const
{
    return CSE_ARRAY_LEN(m_data);
}

TBool TCharArray::isEmpty() const
{
    return len() <= 0;
}

TInt TCharArray::compare(const TCharArray& sOther) const
{
    TCharBuffLocker lock;
    return StrNCompare(m_data,len(),sOther.m_data,sOther.len());
}

TInt TCharArray::compare(const TXchar *pOther) const
{
    TCharBuffLocker lock;
    int iLen = (pOther == NULL)? 0:STRLEN(pOther);
    return StrNCompare(m_data,len(),pOther,iLen);
}

TCharArray TCharArray::slice(int index, int iLen) const
{
    TCharBuffLocker lock;

    int nLen = len();
    if (index < 0)
    {
        index = nLen + index;
        if (index < 0) index = 0;
    }

    if (index >= nLen || iLen == 0)
        return TCharArray();
    else
    {
        int iLeft = nLen - index;  // max left can copied
        if (iLen == -1 || iLen > iLeft) iLen = iLeft;
        return TCharArray(m_data + index,iLen);
    }
}

TXchar *TCharArray::transfer(TXchar *pOther)
{
    TXchar *ret = m_data;
    if (NULL == pOther)
        m_data = __EmptyString;
    else
    {
        TUint32 *pLen = (TUint32 *)((char *)pOther - sizeof(TUint32[2]));
        ASSERT_TRUE(pLen[0] >= pLen[1]);

        m_data = pOther;
    }
    return ret;
}

void TCharArray::exchange(TCharArray& other)
{
    TCharBuffLocker lock;
    other.transfer(transfer(other.addr()));
}

TBool TCharArray::copyFrom(const TXchar* pSour, int iLen, int iOff) const
{
    if (iOff + iLen > len()) return false;

    TXchar *pTarg = m_data + iOff;
    if (pTarg > pSour)
    {   // avoid backward overwrite
        pTarg += iLen - 1;
        pSour += iLen - 1;
        for (int i = iLen-1; i >= 0; i--,pTarg--,pSour--)
            *pTarg = *pSour;
    }
    else if (pTarg != pSour)
    {   // avoid forward overwrite
        for (int i = 0; i < iLen; i++,pTarg++,pSour++)
            *pTarg = *pSour;
    }
    // else, pTarg == pSour, // no need copy
    return true;
}

void TCharArray::insertEx(int iPos, const TXchar *pSour, int iLen)
{
    TUint32 *pLen = (TUint32 *)((char *)m_data - sizeof(TUint32[2]));
    int iTargLen = pLen[1];

    if (iPos == iTargLen)
    {
        joinEx(pSour,iLen);
        return;
    }
    
    int iTotal = iTargLen + iLen;

    if ((int)pLen[0] >= iTargLen + iLen)  // space is enough
    {
        pLen[1] = iTotal;
        copyFrom(m_data + iPos,iTargLen - iPos,iPos + iLen);
        copyFrom(pSour,iLen,iPos);
        m_data[iTotal] = _T('\0');
    }
    else
    {
        TCharArray ret(_T('\0'),iTotal);
        if (iPos > 0)
            ret.copyFrom(m_data,iPos,0);
        ret.copyFrom(pSour,iLen,iPos);
        ret.copyFrom(m_data + iPos,iTargLen - iPos,iPos + iLen);
        
        // exchange result and this
        ret.m_data = transfer(ret.m_data);
    }
}

TXchar TCharArray::append(TXchar ch)
{
    join(&ch,1);
    return ch;
}

TCharArray& TCharArray::add(TXchar ch, int *pIndex)
{
    TCharBuffLocker lock;
    if (pIndex != NULL) *pIndex = len();
    return joinEx(&ch,1);
}

const TIntObj& TCharArray::insert(int index, const TIntObj& obj)
{
    TCharBuffLocker lock;

    int nLen = len();
    if (index < 0)
    {
        index = index + nLen;
        if (index < 0) index = 0;
    }
    if (index > nLen) index = nLen;

    insertEx(index,(const TXchar*)&obj,1);
    return obj;
}

void TCharArray::insertSlice(int index, const TXchar *pSour, int iLen)
{
    TCharBuffLocker lock;
    int iLen_ = (iLen <= -1)? ((pSour == NULL)? 0:STRLEN(pSour)): iLen;
    if (iLen_ <= 0) return;
    
    int nLen = len();
    if (index < 0)
    {
        index = index + nLen;
        if (index < 0) index = 0;
    }
    if (index > nLen) index = nLen;
    
    insertEx(index,pSour,iLen_);
}

void TCharArray::remove(int index, int iLen)
{
    TCharBuffLocker lock;

    int nLen = len();
    if (index < 0)
    {
        index = index + nLen;
        if (index < 0) index = 0;
    }
    if (index >= nLen || iLen <= 0) return;

    int iLeft = nLen - index;     // max iLeft items can be deleted
    if (iLen > iLeft) iLen = iLeft;
    int iNewLen = nLen - iLen;
    
    if (iNewLen <= 0)
    {   // free old buffer
        TryFreeCharBlock(m_data);
        m_data = __EmptyString;
    }
    else
    {
        copyFrom(m_data + index + iLen,nLen - index - iLen,index);
        truncate(iNewLen);
    }
}

void TCharArray::clear()
{
    TCharBuffLocker lock;
    TryFreeCharBlock(m_data);
    m_data = __EmptyString;
}

int TCharArray::truncate(int iLen) const
{
    TCharBuffLocker lock;

    int nLen = len();
    if (iLen < 0) iLen += nLen;
    if (iLen < 0) iLen = 0;
    
    if (iLen < nLen)
    {
        m_data[iLen] = _T('\0');
        TUint32 *pNewLen = (TUint32 *)((char *)m_data - sizeof(TUint32));
        *pNewLen = iLen;

        return iLen;
    }
    else return nLen;
}

STATIC int FindSortIndex(const TXchar *m_data, int iHigh, TXchar ch)
{
    int iLow = 0;
    int index = -1;
    while (iLow <= iHigh)
    {
        int iHalf = (iLow + iHigh) / 2;
        TInt i = (TInt)m_data[iHalf] - (TInt)ch;

        if (i < 0)
            iLow = iHalf + 1;
        else if (i == 0)
        {
            index = iHalf;
            break;
        }
        else iHigh = iHalf - 1;
    }
    if (index == -1) index = iLow;

    return index;
}

int TCharArray::sortAdd(TXchar ch)
{
    TCharBuffLocker lock;

    int index = FindSortIndex(m_data,len()-1,ch);
    insertEx(index,&ch,1);
    return index;
}

void TCharArray::sort() const
{
    TCharBuffLocker lock;

    int iCount;
    int iLen = len();
    if (iLen <= 1) return; // no need to sort

    // first make empty-string-copy and sort-add every item
    TCharArray sTmp(NULL,0,iLen);
    TXchar *sSour = addr();
    TXchar *sTarg = sTmp.addr();
    for (iCount = 0; iCount < iLen; iCount++,sSour++)
        sTmp.insertEx(FindSortIndex(sTarg,iCount-1,*sSour),sSour,1);

    // then copy back context
    sSour = sTarg; sTarg = addr(); sSour = sTmp.addr();
    for (iCount = 0; iCount < iLen; iCount++,sSour++,sTarg++)
        *sTarg = *sSour;
}

TCharArray TCharArray::vformat(const TXchar *frmt, va_list ArgPtr)
{
    TXchar buff[CSE_FORMAT_BUFF];
    int iLen = VSPRINTF(buff,frmt,ArgPtr);
    ASSERT_MSG(iLen < CSE_FORMAT_BUFF,_T("out of print buffer range"));

    return TCharArray(buff,iLen);
}

TCharArray TCharArray::format(const TXchar *frmt, ...)
{
    va_list ArgPtr;
    va_start(ArgPtr,frmt);

    TXchar buff[CSE_FORMAT_BUFF];
    int iLen = VSPRINTF(buff,frmt,ArgPtr);
    va_end(ArgPtr);
    ASSERT_MSG(iLen < CSE_FORMAT_BUFF,_T("out of print buffer range"));

    return TCharArray(buff,iLen);
}

void TCharArray::resize(int iSize)
{
    TCharBuffLocker lock;

    TUint32 *pLen = (TUint32 *)((char *)m_data - sizeof(TUint32[2]));
    int iCurrSize = pLen[0];

    if (iCurrSize >= iSize)  // space is enough
        pLen[1] = iSize;
    else
    {   // create enlarged new string
        TCharArray ret(m_data,pLen[1],iSize - iCurrSize);
        ret.m_data = transfer(ret.m_data); // exchange
        
        TUint32 *pNewLen = (TUint32 *)((char *)m_data - sizeof(TUint32));
        *pNewLen = iSize;
    }
    m_data[iSize] = _T('\0');
}

TUint32 TCharArray::hash() const
{
    TCharBuffLocker lock;

    TUint32 ret;
    TUint32 size = len();
    int iMaxLoop = 28;

    if (size <= 5)
    {
        ret = (_T('#') ^ m_data[0]) << 12;
        int iLoop = 0;
        for (int iCount = size-1; iCount >= 1; iCount--)
        {
            ret = ret ^ ((_T('0') ^ m_data[iCount]) << iLoop);
            iLoop += 4; // #1234, char4(bit0~3) char3(bit4~7) char2(bit8~11) char1(12~15)
            if (iLoop > iMaxLoop) iLoop = 0;
        }
    }
    else
    {
        size = size * sizeof(TXchar);     // TXchar maybe _UNICODE
        int iLoop = size / sizeof(TUint32);
        if (iLoop > STR_MARSH_LOOP_COUNT) // max use STR_MARSH_LOOP_COUNT * 4 bytes
            iLoop = STR_MARSH_LOOP_COUNT;

        ret = size;
        TUint32 *p = (TUint32 *)m_data;
        for (int iCount = 0; iCount < iLoop; iCount++, p++)
            ret ^= *p;
    }

    return ret;
}

TCharArray TCharArray::str(TXchar pre) const
{
    TCharBuffLocker lock;

    int iLen = len();
    TCharArray ret = pre;

    if (0 == iLen)
    {
        ret += pre;
        return ret;
    }

    TBool inEscape = false;
    for (int iCount=0; iCount < iLen; iCount++)
    {
        TXchar ch = m_data[iCount];

        if (inEscape)
        {
            ret += ch;
            inEscape = false;
        }
        else if (ch == _T('\\'))
        {
            inEscape = true;
            ret += _T('\\');
            ret += ch;
        }
        else if (ch == pre)
        {
            ret += _T('\\');
            ret += ch;
        }
        else if ( ch == _T('\t') )
            ret += _T("\\t");
        else if ( ch == _T('\n') )
            ret += _T("\\n");
        else if ( ch == _T('\v') )
            ret += _T("\\v");
        else if ( ch == _T('\f') )
            ret += _T("\\f");
        else if ( ch == _T('\r') )
            ret += _T("\\r");
        else if ( ch == _T('\0') )
            ret += _T("\\0");
        else
        {
#ifdef _UNICODE
            ret += ch;
#else
            if (ch < ' ' || ch >= 127)
                ret += format(_T("\\x%.2x"),(unsigned char)ch);
            else ret += ch;
#endif
        }
    }
    ret += pre;

    return TCharArray(CUT_FROM,ret);
}

TBool TCharArray::toInt(TInt& ret) const
{
    ret = 0;
    TInt64 iTmp = 0;                 // "%I64i" "%lli"
    if (SSCANF(m_data,_T("%") CseLongLongFmtSpec _T("i"),&iTmp) != 0)
    {
        ret = (TInt)iTmp;
        return true;
    }
    else return false;
}

TBool TCharArray::toWint(TInt64& ret) const
{
    ret = 0;                         // "%I64i" "%lli"
    return SSCANF(m_data,_T("%") CseLongLongFmtSpec _T("i"),&ret) != 0;
}

TBool TCharArray::toDouble(TDouble& ret) const
{
    TXchar *RetPtr = m_data;
    ret = STRTOD(m_data,&RetPtr);
    return RetPtr > m_data;
}

const TCharArray& TCharArray::upper() const
{
    TCharBuffLocker lock;

    int iLen = len();
    if (iLen == 0) return *this; // no need uppercase
    
    TXchar *pTarg = m_data;
    TXchar offset = _T('a') - _T('A');
    for (int i = 0; i < iLen; i++,pTarg++)
    {
        TXchar ch = *pTarg;
        if (ch >= _T('a') && ch <= _T('z'))
            *pTarg = ch - offset;
    }
    return *this;
}

const TCharArray& TCharArray::lower() const
{
    TCharBuffLocker lock;

    int iLen = len();
    if (iLen <= 0) return *this; // no need lowercase

    TXchar *pTarg = m_data;
    TXchar offset = _T('a') - _T('A');
    for (int i = 0; i < iLen; i++,pTarg++)
    {
        TXchar ch = *pTarg;
        if (ch >= _T('A') && ch <= _T('Z'))
            *pTarg = ch + offset;
    }
    return *this;
}

int TCharArray::trim() const
{
    TCharBuffLocker lock;
    
    int iLen = len();
    if (iLen == 0) return iLen; // no need trim

    // first, trim right
    int i;
    int iCount = 0;
    for (i = iLen-1; i >= 0; i--)
    {
        TXchar ch = m_data[i];
        if ( ch == _T(' ') || ch == _T('\t') || ch == _T('\n') ||
             ch == _T('\r') || ch == _T('\v') || ch == _T('\f') )
            iCount++;
        else break;
    }
    if (iCount > 0)
    {
        iLen -= iCount;
        truncate(iLen);
    }

    // then, trim left
    iCount = 0;
    for (i = 0; i < iLen; i++)
    {
        TXchar ch = m_data[i];
        if ( ch == _T(' ') || ch == _T('\t') || ch == _T('\n') ||
             ch == _T('\r') || ch == _T('\v') || ch == _T('\f') )
            iCount++;
        else break;
    }
    if (iCount > 0)
    {
        iLen -= iCount;
        copyFrom(m_data+iCount,iLen,0);
        truncate(iLen);
    }

    return iLen;
}

int TCharArray::trimLeft() const
{
    TCharBuffLocker lock;

    int iLen = len();
    if (iLen <= 0) return iLen; // no need trim

    int iCount = 0;
    for (int i = 0; i < iLen; i++)
    {
        TXchar ch = m_data[i];
        if ( ch == _T(' ') || ch == _T('\t') || ch == _T('\n') ||
             ch == _T('\r') || ch == _T('\v') || ch == _T('\f') )
            iCount++;
        else break;
    }
    if (iCount > 0)
    {
        iLen -= iCount;
        copyFrom(m_data + iCount,iLen,0);
        truncate(iLen);
    }

    return iLen;
}

int TCharArray::trimRight() const
{
    TCharBuffLocker lock;

    int iLen = len();
    if (iLen <= 0) return iLen; // no need trim

    int iCount = 0;
    for (int i = iLen-1; i >= 0; i--)
    {
        TXchar ch = m_data[i];
        if ( ch == _T(' ') || ch == _T('\t') || ch == _T('\n') ||
             ch == _T('\r') || ch == _T('\v') || ch == _T('\f') )
            iCount++;
        else break;
    }
    if (iCount > 0)
    {
        iLen -= iCount;
        truncate(iLen);
    }
    
    return iLen;
}

int TCharArray::find(TXchar ch, int from) const
{
    TCharBuffLocker lock;

    int iLen = len();
    if (from >= iLen) return -1;

    if (from < 0) from = iLen + from;
    if (from < 0) from = 0;

    TXchar *pChar = m_data + from;
    for (int i = from; i < iLen; i++, pChar++)
    {
        if (*pChar == ch)
            return i;
    }
    return -1;
}

int TCharArray::find(const TXchar *pSub, int from) const
{
    TCharBuffLocker lock;

    int iLen = len();
    
    if (from < 0) from += iLen;
    if (from >= iLen) return -1;
    if (from < 0) from = 0;

    TXchar *sTarg = m_data + from;
    TXchar *s = STRSTR(sTarg,pSub);
    if (s != NULL)
        return s - m_data;
    else return -1;
}

TBool TCharArray::enumItem(void*& pIter, TXchar *pRet) const
{
    TCharBuffLocker lock;

    if (pIter == NULL)  // start enumlate
        pIter = m_data;
    else pIter = (TXchar *)pIter + 1;

    // maybe the dict has resize
    if ((TXchar *)pIter < m_data) return false;
    TXchar *pEnd = m_data + CSE_ARRAY_LEN(m_data);
    if ((TXchar *)pIter < pEnd)
    {
        if (pRet != NULL) *pRet = *(TXchar *)pIter;
        return true;
    }
    else return false;
}

int TCharArray::count(const TXchar* pSub, int iStart, int iEnd) const
{
    if (pSub == NULL) return 0;
    
    TCharBuffLocker lock;
    int iSeg = STRLEN(pSub);
    if (iSeg == 0) return 0;

    int iLen = len();
    
    if (iStart < 0) iStart += iLen;
    if (iStart >= iLen) return 0;
    if (iStart < 0) iStart = 0;

    if (iEnd < 0)
    {
        iEnd += iLen;
        if (iEnd < 0) return 0;
    }
    else if (iEnd == 0)
        iEnd = iLen;
    if (iEnd > iLen) iEnd = iLen;

    int iNum = 0;
    TXchar *sTarg = m_data + iStart;
    TXchar *sEnd  = m_data + iEnd;
    TXchar *s; 
    while ((s = STRSTR(sTarg,pSub)) != NULL)
    {
        sTarg = s + iSeg;
        if (sTarg < sEnd)
            iNum++;
        else if (sTarg == sEnd)
        {
            iNum++;
            break;
        }
        else break;
    }

    return iNum;
}

TCharArray& TCharArray::align(int iWidth, TXchar fillChar)
{
    TCharBuffLocker lock;

    TUint32 *pLen = (TUint32 *)((char *)m_data - sizeof(TUint32[2]));
    int iOldLen = pLen[1];
    if (iWidth <= iOldLen)
        return *this;

    if ((int) pLen[0] >= iWidth)  // space is enough
        pLen[1] = iWidth;
    else
    {   // create enlarged new string
        TCharArray ret(m_data,iOldLen,iWidth - iOldLen);
        ret.m_data = transfer(ret.m_data); // exchange
        
        TUint32 *pNewLen = (TUint32 *)((char *)m_data - sizeof(TUint32));
        *pNewLen = iWidth;
    }
    m_data[iWidth] = _T('\0');

    int iPos = (iWidth - iOldLen) / 2;
    int i;
    for (i = iWidth - 1; i >= iOldLen + iPos; i--)
        m_data[i] = fillChar;

    if (iPos)
    {
        for (i = iOldLen + iPos - 1; i >= iPos; i--)
            m_data[i] = m_data[i-iPos];
        for (i = iPos - 1; i >= 0; i--)
            m_data[i] = fillChar;
    }

    return *this;
}

TCharArray& TCharArray::alignLeft(int iWidth, TXchar fillChar)
{
    TCharBuffLocker lock;

    TUint32 *pLen = (TUint32 *)((char *)m_data - sizeof(TUint32[2]));
    int iOldLen = pLen[1];
    if (iWidth <= iOldLen)
        return *this;

    if ((int) pLen[0] >= iWidth)  // space is enough
        pLen[1] = iWidth;
    else
    {   // create enlarged new string
        TCharArray ret(m_data,iOldLen,iWidth - iOldLen);
        ret.m_data = transfer(ret.m_data); // exchange
        
        TUint32 *pNewLen = (TUint32 *)((char *)m_data - sizeof(TUint32));
        *pNewLen = iWidth;
    }
    m_data[iWidth] = _T('\0');

    for (int i = iWidth - 1; i >= iOldLen; i--)
        m_data[i] = fillChar;

    return *this;
}

TCharArray& TCharArray::alignRight(int iWidth, TXchar fillChar)
{
    TCharBuffLocker lock;

    TUint32 *pLen = (TUint32 *)((char *)m_data - sizeof(TUint32[2]));
    int iOldLen = pLen[1];
    if (iWidth <= iOldLen)
        return *this;

    if ((int) pLen[0] >= iWidth)  // space is enough
        pLen[1] = iWidth;
    else
    {   // create enlarged new string
        TCharArray ret(m_data,iOldLen,iWidth - iOldLen);
        ret.m_data = transfer(ret.m_data); // exchange
        
        TUint32 *pNewLen = (TUint32 *)((char *)m_data - sizeof(TUint32));
        *pNewLen = iWidth;
    }
    m_data[iWidth] = _T('\0');

    int iPos = iWidth - iOldLen;
    int i;
    for (i = iWidth - 1; i >= iPos; i--)
        m_data[i] = m_data[i-iPos];
    for (i = iPos - 1; i >= 0; i--)
        m_data[i] = fillChar;

    return *this;
}

TCharArray& TCharArray::replace(const TXchar *sSub,const TXchar *sNew,int count)
{
    if (sSub == NULL || count == 0) return *this;

    TCharBuffLocker lock;
    int SubLen = STRLEN(sSub);
    if (SubLen == 0) return *this;

    TUint32 *pLen = (TUint32 *)((char *)m_data - sizeof(TUint32[2]));
    int iOldLen = pLen[1];
    TIntArray founds;

    TXchar *sTarg = m_data;
    TXchar *sEnd = m_data + iOldLen;
    TXchar *s;
    while ((s = STRSTR(sTarg,sSub)) != NULL)
    {
        sTarg = s + SubLen;
        if (sTarg < sEnd)
            founds.append((TInt) (s - m_data));
        else if (sTarg == sEnd)
        {
            founds.append((TInt) (s - m_data));
            break;
        }
        else break;

        if (count > 0 && founds.len() >= count) break; // max replace 'count' sSub
    }

    int FoundNum = founds.len();
    if (FoundNum)
    {
        int NewLen = STRLEN(sNew);
        if (NewLen == SubLen)
        {
            for (int i = FoundNum - 1; i >= 0; i--)
                STRNCPY(m_data + founds[i],sNew,NewLen);
        }

        else if (NewLen > SubLen)
        {   // enlarge string
            int iWidth = iOldLen + (NewLen - SubLen) * FoundNum;

            if ((int) pLen[0] >= iWidth)  // space is enough
                pLen[1] = iWidth;
            else
            {   // create enlarged new string
                TCharArray ret(m_data,iOldLen,iWidth - iOldLen);
                ret.m_data = transfer(ret.m_data); // exchange
        
                TUint32 *pNewLen = (TUint32 *)((char *)m_data - sizeof(TUint32));
                *pNewLen = iWidth;
            }
            m_data[iWidth] = _T('\0');

            int iOldLast = iOldLen-1;
            int iNewLast = iWidth-1;
            for (int i = FoundNum - 1; i >= 0; i--)
            {
                int iPos = founds[i] + SubLen;
                while (iOldLast >= iPos)
                {
                    m_data[iNewLast] = m_data[iOldLast];
                    iOldLast--; iNewLast--;
                }

                for (int i2 = NewLen-1; i2 >= 0; i2--,iNewLast--)
                    m_data[iNewLast] = sNew[i2];
                iOldLast -= SubLen;
            }

            ASSERT_TRUE(iNewLast == iOldLast);
        }

        else // NewLen < SubLen
        {
            int iWidth = iOldLen - (SubLen - NewLen) * FoundNum;
            pLen[1] = iWidth;

            int iOldLast = 0;
            int iNewLast = 0;
            for (int i = 0; i < FoundNum; i++)
            {
                int iPos = founds[i];
                if (i == 0)
                {
                    iNewLast = iPos;
                    iOldLast = iPos;
                }
                else
                {
                    while (iOldLast < iPos)
                    {
                        m_data[iNewLast] = m_data[iOldLast];
                        iOldLast++; iNewLast++;
                    }
                }

                for (int i2 = 0; i2 < NewLen; i2++,iNewLast++)
                    m_data[iNewLast] = sNew[i2];
                iOldLast += SubLen;
            }

            while (iOldLast < iOldLen)
            {
                m_data[iNewLast] = m_data[iOldLast];
                iOldLast++; iNewLast++;
            }

            ASSERT_TRUE(iNewLast == iWidth);
            m_data[iWidth] = _T('\0');
        }
    }

    return *this;
}

TObjArray TCharArray::split(const TXchar* sSep, int count) const
{
    TObjArray bRet;
    if (count == 0) return bRet;

    TCharBuffLocker lock;
    int iLen = len();
    if (iLen == 0) return bRet;
    TXchar *sEnd  = m_data + iLen;

    if (sSep == NULL || *sSep == _T('\0'))
    {   // split with white space
        TXchar *sTarg = m_data;
        TXchar *sLast = m_data;

        TBool LastIsWhite = true;
        TXchar ch;
        while ((ch = *sTarg) != 0)
        {
            sTarg++;

            TBool added = false;
            if ( ch == _T(' ') || (ch >= _T('\t') && ch <= _T('\r')) )
            {   // current is white space
                if (! LastIsWhite)
                {
                    int iOneLen = (int) (sTarg - sLast - 1);
                    if (iOneLen)
                    {
                        TCharArray sTmp = TCharArray(sLast,iOneLen);
                        bRet.append(TCseObj(CUT_FROM,sTmp));
                        added = true;
                    }

                    LastIsWhite = true;
                }
                // else, LastIsWhite = true;
                sLast = sTarg;
            }
            else
            {   // current is not white space
                if (LastIsWhite)
                {
                    sLast = sTarg - 1;
                    LastIsWhite = false;
                }
                // else, LastIsWhite = false
            }

            if (sTarg == sEnd)
            {
                if (sLast >= sEnd)
                    sLast = NULL;
                break;
            }
            else if (sTarg > sEnd || (added && count > 0 && bRet.len() >= count))
            {
                sLast = NULL;
                break;
            }
        }

        if (sLast != NULL)
        {
            int iOneLen = (int) (sEnd - sLast);
            if (iOneLen)
            {
                TCharArray sTmp = TCharArray(sLast,iOneLen);
                bRet.append(TCseObj(CUT_FROM,sTmp));
            }
        }
    }

    else
    {
        int iSeg = STRLEN(sSep);
        if (iSeg == 0) return bRet;
    
        TXchar *sTarg = m_data;
        TXchar *sNext = NULL;
        TXchar *s; 
        while ((s = STRSTR(sTarg,sSep)) != NULL)
        {
            TBool added = false;
            sNext = s + iSeg;

            int iOneLen = (int) (s - sTarg);
            if (iOneLen)
            {
                TCharArray sTmp = TCharArray(sTarg,iOneLen);
                bRet.append(TCseObj(CUT_FROM,sTmp));
                added = true;
            }

            if (sNext >= sEnd)
            {
                sNext = NULL;
                break;
            }
            else sTarg = sNext;
            
            if (added && count > 0 && bRet.len() >= count)
            {
                sNext = NULL;
                break;
            }
        }

        if (sNext != NULL)
        {
            int iOneLen = (int) (sEnd - sNext);
            TCharArray sTmp = TCharArray(sNext,iOneLen);
            bRet.append(TCseObj(CUT_FROM,sTmp));
        }
    }

    return TObjArray(CUT_FROM,bRet);
}

TObjArray TCharArray::splitLines(TBool keepEnds, int count) const
{
    TObjArray bRet;
    if (count == 0) return bRet;
    TCharBuffLocker lock;

    int iLen = len();
    if (iLen == 0) return bRet;
    TXchar *sEnd  = m_data + iLen;

    // split with white space
    TXchar *sTarg = m_data;
    TXchar *sLast = m_data;
    TXchar ch;

    while ((ch = *sTarg) != 0)
    {
        sTarg++;
        if ( ch == _T('\r') || ch == _T('\n') )
        {   // current is white space
            int iExt = 1;
            int iOneLen = (int) (sTarg - sLast);
            if (ch == _T('\r') && *sTarg == _T('\n'))
            {
                sTarg++;
                iExt++;
                iOneLen++;
            }

            if (! keepEnds) iOneLen -= iExt;
            if (iOneLen == 0)
                bRet.append(CseEmptyStr);
            else
            {
                TCharArray sTmp = TCharArray(sLast,iOneLen);
                bRet.append(TCseObj(CUT_FROM,sTmp));
            }

            if (sTarg >= sEnd || (count > 0 && bRet.len() >= count))
            {
                sLast = NULL;
                break;
            }
            else sLast = sTarg;
        }
        
        if (sTarg == sEnd)
        {
            if (sLast >= sEnd)
                sLast = NULL;
            break;
        }
        else if (sTarg > sEnd)
        {
            sLast = NULL;
            break;
        }   
    }

    if (sLast != NULL)
    {
        int iOneLen = (int) (sEnd - sLast);
        if (iOneLen)
        {
            TCharArray sTmp = TCharArray(sLast,iOneLen);
            bRet.append(TCseObj(CUT_FROM,sTmp));
        }
    }

    return TObjArray(CUT_FROM,bRet);
}

STATIC TCharArray JoinTwoCharBuff(const TXchar *s1, int iLen1, const TXchar *s2, int iLen2)
{
    int iLen = iLen1 + iLen2;

    if (iLen == 0)
        return TCharArray();
    else
    {
        int iPad = (iLen + 1) / 2;
        TCharArray ret(s1,iLen1,iLen2+iPad);
        ret.join(s2,iLen2);
        return TCharArray(CUT_FROM,ret);
    }
}

TCharArray TCharArray::operator+(const TCharArray& y) const
{
    TCharBuffLocker lock;
    return JoinTwoCharBuff(addr(),len(),y.addr(),y.len());
}

TCharArray TCharArray::operator+(const TXchar *y) const
{
    TCharBuffLocker lock;
    int iLen = (y == NULL)? 0: STRLEN(y);
    return JoinTwoCharBuff(addr(),len(),y,iLen);
}

TCharArray TCharArray::operator+(TXchar y) const
{
    TCharBuffLocker lock;
    return JoinTwoCharBuff(addr(),len(),&y,1);
}

TCharArray operator+(TXchar x, const TCharArray& y)
{
    TCharBuffLocker lock;
    return JoinTwoCharBuff(&x,1,y.addr(),y.len());
}

TCharArray operator+(const TXchar *x, const TCharArray& y)
{
    TCharBuffLocker lock;
    int iLen = (x == NULL)? 0: STRLEN(x);
    return JoinTwoCharBuff(x,iLen,y.addr(),y.len());
}

TBool TCharArray::operator==(const TCharArray& y) const
{
    TCharBuffLocker lock;
    return StrNCompare(addr(),len(),y.addr(),y.len()) == 0;
}

TBool TCharArray::operator==(const TXchar *y) const
{
    TCharBuffLocker lock;
    int iLen = (y == NULL)? 0: STRLEN(y);
    return StrNCompare(addr(),len(),y,iLen) == 0;
}

TBool operator==(const TXchar *x, const TCharArray& y)
{
    TCharBuffLocker lock;
    int iLen = (x == NULL)? 0: STRLEN(x);
    return StrNCompare(x,iLen,y.addr(),y.len()) == 0;
}

TBool TCharArray::operator!=(const TCharArray& y) const
{
    TCharBuffLocker lock;
    return StrNCompare(addr(),len(),y.addr(),y.len()) != 0;
}

TBool TCharArray::operator!=(const TXchar *y) const
{
    TCharBuffLocker lock;
    int iLen = (y == NULL)? 0: STRLEN(y);
    return StrNCompare(addr(),len(),y,iLen) != 0;
}

TBool operator!=(const TXchar *x, const TCharArray& y)
{
    TCharBuffLocker lock;
    int iLen = (x == NULL)? 0: STRLEN(x);
    return StrNCompare(x,iLen,y.addr(),y.len()) != 0;
}

TBool TCharArray::operator>(const TCharArray& y) const
{
    TCharBuffLocker lock;
    return StrNCompare(addr(),len(),y.addr(),y.len()) > 0;
}

TBool TCharArray::operator>(const TXchar *y) const
{
    TCharBuffLocker lock;
    int iLen = (y == NULL)? 0: STRLEN(y);
    return StrNCompare(addr(),len(),y,iLen) > 0;
}

TBool operator>(const TXchar *x, const TCharArray& y)
{
    TCharBuffLocker lock;
    int iLen = (x == NULL)? 0: STRLEN(x);
    return StrNCompare(x,iLen,y.addr(),y.len()) > 0;
}

TBool TCharArray::operator>=(const TCharArray& y) const
{
    TCharBuffLocker lock;
    return StrNCompare(addr(),len(),y.addr(),y.len()) >= 0;
}

TBool TCharArray::operator>=(const TXchar *y) const
{
    TCharBuffLocker lock;
    int iLen = (y == NULL)? 0: STRLEN(y);
    return StrNCompare(addr(),len(),y,iLen) >= 0;
}

TBool operator>=(const TXchar *x, const TCharArray& y)
{
    TCharBuffLocker lock;
    int iLen = (x == NULL)? 0: STRLEN(x);
    return StrNCompare(x,iLen,y.addr(),y.len()) >= 0;
}

TBool TCharArray::operator<(const TCharArray& y) const
{
    TCharBuffLocker lock;
    return StrNCompare(addr(),len(),y.addr(),y.len()) < 0;
}

TBool TCharArray::operator<(const TXchar *y) const
{
    TCharBuffLocker lock;
    int iLen = (y == NULL)? 0: STRLEN(y);
    return StrNCompare(addr(),len(),y,iLen) < 0;
}

TBool operator<(const TXchar *x, const TCharArray& y)
{
    TCharBuffLocker lock;
    int iLen = (x == NULL)? 0: STRLEN(x);
    return StrNCompare(x,iLen,y.addr(),y.len()) < 0;
}

TBool TCharArray::operator<=(const TCharArray& y) const
{
    TCharBuffLocker lock;
    return StrNCompare(addr(),len(),y.addr(),y.len()) <= 0;
}

TBool TCharArray::operator<=(const TXchar *y) const
{
    TCharBuffLocker lock;
    int iLen = (y == NULL)? 0: STRLEN(y);
    return StrNCompare(addr(),len(),y,iLen) <= 0;
}

TBool operator<=(const TXchar *x, const TCharArray& y)
{
    TCharBuffLocker lock;
    int iLen = (x == NULL)? 0: STRLEN(x);
    return StrNCompare(x,iLen,y.addr(),y.len()) <= 0;
}
